package com.nexfra.vocsys.common.utils;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.stereotype.Service;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.multipart.MultipartFile;

import com.nexfra.vocsys.common.domain.AttachFile;

/**
 * 파일 업로드 및 다운로드를 관리하는 클래스이다.
 *
 * @author cjs
 * @since 1.0
 */
@Service("fileManagement")
public class FileManagement {
    /**
     * Image의 확장자를 저장한다.
     */
    private static List<String> imageExtensions = new ArrayList<String>();

    /**
     * EXE의 확장자를 저장한다.
     */
    private static List<String> exeExtensions = new ArrayList<String>();

    /**
     * Document의 확장자를 저장한다.
     */
    private static List<String> docExtensions = new ArrayList<String>();

    /**
     * 위젯의 확장자를 저장한다.
     */
    private static List<String> widgetExtensions = new ArrayList<String>();

    /**
     * 기타 확장자를 저장한다.
     */
    private static List<String> etcExtensions = new ArrayList<String>();

	/**
     * 기타 확장자를 저장한다.
     */
    private static List<String> zipExtensions = new ArrayList<String>();

    static {
        imageExtensions.addAll(Arrays.asList(new String[]{"gif", "jpg", "jpeg", "png", "bmp"}));
        exeExtensions.addAll(Arrays.asList(new String[]{"exe","xpi"}));
        docExtensions.addAll(Arrays.asList(new String[]{"xls", "xlsx", "pdf", "doc", "docx", "ppt", "pptx", "txt", "hwp"}));
        widgetExtensions.addAll(Arrays.asList(new String[]{"wgt"}));
        etcExtensions.addAll(Arrays.asList(new String[]{"etc"}));
        zipExtensions.addAll(Arrays.asList(new String[]{"rar","zip","war","jar"}));
    }

    /**
     * 파일을 업로드 한다.
     * <xmp>
     * 예)
     * MultipartHttpServletRequest multipartReq = (MultipartHttpServletRequest)req;
     * Map<String, MultipartFile> files = multipartReq.getFileMap();
     * FileManagement.uploadFile(req,"/imageFile",files);
     * </xmp>
     *
     * @param req
     * @param saveFileLocation 저장될 위치 (image인경우는 cntext_root/saveFileLocation 에 저장된다.)
     * @param files            MultipartFile 리스트
     * @param saveFileNames    saveFileNames 저장될 파일이름
     * @param fileSize         제한할 파일사이즈
     * @throws Exception
     */
    public static List<AttachFile> uploadFile(HttpServletRequest req, String saveFileLocation
    		, Map<String, MultipartFile> files
    		, List<String> saveFileNames, long fileSize) throws Exception {
    	String fileName = null;
    	String saveFileName = null;
    	String tempSaveFileLocation = saveFileLocation;
    	File dir = null;
    	int cnt = 0;
    	
    	if (FileManagement.checkExeedFileSize(files, fileSize)) {
    		throw new org.springframework.web.multipart.MaxUploadSizeExceededException(fileSize);
    	}
    	
    	List<AttachFile> attachFiles = new ArrayList<AttachFile>();
    	
    	int ii = 1;
    	for (MultipartFile file : files.values()) {
    		tempSaveFileLocation = getDefaultRepoRootPath() + File.separator + saveFileLocation + File.separator + DateUtil.getCurrentYyyymmdd();
    		
    		if (!"".equals(file.getOriginalFilename())) {
    			fileName = file.getOriginalFilename();

    			if(saveFileNames != null) {
    				saveFileName = saveFileNames.get(cnt++);
    			} else {
    				saveFileName = getDefaultConvertedFileName()+(ii++);// 기본 파일리네임 규칙을 적용
    			}
    			
    			dir = new File(tempSaveFileLocation);
    			if (!dir.exists()) {
    				dir.mkdirs();
    			}
    			FileCopyUtils.copy(file.getInputStream(), new FileOutputStream(tempSaveFileLocation + File.separator + saveFileName));
    		
    			// 파일 object를 생성하여 리스트에 추가
    			AttachFile attachFile = new AttachFile();
    			attachFile.setOriginFileName(fileName);
    			attachFile.setAliasFileName(saveFileName);
    			attachFile.setFileSize(file.getSize()+"");
    			attachFile.setFileExtension(getFileExtension(fileName));
    			attachFile.setFileTotalPath(tempSaveFileLocation + File.separator + saveFileName);
    			attachFiles.add(attachFile);
    		}
    	}
    	return attachFiles;
    }

    
    /**
     * 파일을 업로드 한다.
     * <xmp>
     * 예)
     * MultipartHttpServletRequest multipartReq = (MultipartHttpServletRequest)req;
     * Map<String, MultipartFile> files = multipartReq.getFileMap();
     * FileManagement.uploadFile(req,"/imageFile",files);
     * </xmp>
     *
     * @param req
     * @param saveFileLocation 저장될 위치 (image인경우는 cntext_root/saveFileLocation 에 저장된다.)
     * @param files            MultipartFile 리스트
     * @param saveFileNames    saveFileNames 저장될 파일이름
     * @throws Exception
     */
    public static List<AttachFile> uploadFile(HttpServletRequest req, String saveFileLocation
    		, Map<String, MultipartFile> files
    		, List<String> saveFileNames) throws Exception {
    	String fileName = null;
    	String saveFileName = null;
    	String tempSaveFileLocation = saveFileLocation;
    	File dir = null;
    	int cnt = 0;
    	
    	// 10485760 = 10MB
    	if (FileManagement.checkExeedFileSize(files, 10485760)) {
    		throw new org.springframework.web.multipart.MaxUploadSizeExceededException(10485760);
    	}
    	    	
    	List<AttachFile> attachFiles = new ArrayList<AttachFile>();
    	
    	int ii = 1;
    	for (MultipartFile file : files.values()) {
    		tempSaveFileLocation = getDefaultRepoRootPath() + File.separator + saveFileLocation + File.separator + DateUtil.getCurrentYyyymmdd();
    		
    		if (!"".equals(file.getOriginalFilename())) {
    			fileName = file.getOriginalFilename();

    			if(saveFileNames != null) {
    				saveFileName = saveFileNames.get(cnt++);
    			} else {
    				saveFileName = getDefaultConvertedFileName()+(ii++);// 기본 파일리네임 규칙을 적용
    			}
    			
    			dir = new File(tempSaveFileLocation);
    			if (!dir.exists()) {
    				dir.mkdirs();
    			}
    			FileCopyUtils.copy(file.getInputStream(), new FileOutputStream(tempSaveFileLocation + File.separator + saveFileName));
    		
    			// 파일 object를 생성하여 리스트에 추가
    			AttachFile attachFile = new AttachFile();
    			attachFile.setOriginFileName(fileName);
    			attachFile.setAliasFileName(saveFileName);
    			attachFile.setFileSize(file.getSize()+"");
    			attachFile.setFileExtension(getFileExtension(fileName));
    			attachFile.setFileTotalPath(tempSaveFileLocation + File.separator + saveFileName);
    			attachFiles.add(attachFile);    		
    		}
    	}
    	return attachFiles;
    }
    
    /**
     * 기본 파일리네임 규칙을 적용하여 변환된 파일명을 리턴한다.
     * 
     * @return String 변환된 파일명
     */
    public static String getDefaultConvertedFileName() {
    	return "attach_" + System.currentTimeMillis();
    }

//@TODO 외부설정으로 바꿔야 함    
    /**
     * 기본 파일저장소 루트 경로를 리턴한다.
     * 
     * @return String 변환된 파일명
     */
    public static String getDefaultRepoRootPath() {
    	return "D:\\nexfra\\attachRepository";
    }

        
    
    /**
     * 파일을 하나씩 업로드 할경우.
     * @param req
     * @param saveFileLocation
     * @param file
     * @param saveFileName
     * @param fileSize
     * @throws Exception
     */
    public static void uploadToOneFile(HttpServletRequest req, String saveFileLocation, MultipartFile file, String saveFileName, long fileSize) throws Exception {
        //String applicationRealPath = req.getSession().getServletContext().getRealPath("");
        //String webInfoPath = webInfoPath = applicationRealPath + "/WEB-INF";
        String fileName = null;
        String tempSaveFileLocation = saveFileLocation;
        File dir = null;

        if (file.getSize() > fileSize) {
            throw new org.springframework.web.multipart.MaxUploadSizeExceededException(fileSize);
        }

        tempSaveFileLocation = saveFileLocation;
        if (!"".equals(file.getOriginalFilename())) {
            fileName = file.getOriginalFilename();

                tempSaveFileLocation += "/" + FileManagement.checkExtension(fileName);
                dir = new File(tempSaveFileLocation);
                if (!dir.exists()) {
                    dir.mkdirs();
                }
                FileCopyUtils.copy(file.getInputStream(), new FileOutputStream(tempSaveFileLocation + "/" + saveFileName));
        }
    }

    /**
     * 파일의 사이즈를 검사한다.
     *
     * @param files    파일 맵
     * @param fileSize 파일사이즈
     * @return
     */
    private static boolean checkExeedFileSize(Map<String, MultipartFile> files, long fileSize) {
        for (MultipartFile file : files.values()) {
            if (file.getSize() > fileSize) {
                return true;
            }
        }
        return false;
    }

    /**
     * 파일을 다운로드 한다.
     * <xmp>
     * 예)
     * FileManagement.downloadFile(request,response,saveFileLocation,request.getParameter("originalFileName"),request.getParameter("saveFileName"));
     * client
     * <img src="http://localhost:8080/path/saveFileName/> 또는
     * <a href="/download.do?originalFileName=originalFileName&saveFileName=saveFileName/>"
     * </xmp>
     *
     * @param req
     * @param res
     * @param path             다운로드할 위치 (image인경우는 cntext_root/saveFileLocation 에 다운로드 된다.)
     * @param originalFileName 다운로드할 파일명
     * @param saveFileName     실제 서버에 저장된 파일명
     * @throws Exception
     */
    public static void downloadFile(HttpServletRequest req, HttpServletResponse res, String path, String originalFileName, String saveFileName) throws Exception {
        System.out.println("===============downloadFile==================="+req.toString());
    	path += "/" + FileManagement.checkExtension(originalFileName);
        /** Build File Full Path */
        StringBuffer fileFullPath = new StringBuffer();

        fileFullPath.append(path).append(File.separatorChar).append(saveFileName);
        File dwldFile = new File(fileFullPath.toString());

        /** Set Response Header */
        res.setHeader("Content-Type", "application/octet-stream; charset=UTF-8");
        res.setContentLength((int) dwldFile.length());
        res.setHeader("Content-Disposition", "attachment; fileName=" + "\"" + new String(originalFileName.getBytes("MS949"), "8859_1") + "\"" + ";");
        res.setHeader("Content-Transfer-Encoding", "binary");
        OutputStream os = res.getOutputStream();
        InputStream is = null;
        is = new FileInputStream(dwldFile);
        FileCopyUtils.copy(is, os);
        os.flush();
    }

    /**
     * 파일을 다운로드 한다. 파일디렉토리의 경로는 파라미터로 주어진 값을 그대로 사용한다.
     * <xmp>
     * 예)
     * FileManagement.downloadFile(request,response,saveFileLocation,request.getParameter("originalFileName"),request.getParameter("saveFileName"));
     * client
     * <img src="http://localhost:8080/path/saveFileName/> 또는
     * <a href="/download.do?originalFileName=originalFileName&saveFileName=saveFileName/>"
     * </xmp>
     *
     * @param req
     * @param res
     * @param path             다운로드할 위치
     * @param originalFileName 다운로드할 파일명
     * @param saveFileName     실제 서버에 저장된 파일명
     * @throws Exception
     */
    public static void downloadFileOnPath(HttpServletRequest req, HttpServletResponse res, String path, String originalFileName, String saveFileName) throws Exception {
    	System.out.println("===============downloadFile==================="+req.toString());

    	/** Build File Full Path */
    	StringBuffer fileFullPath = new StringBuffer();
    	
    	fileFullPath.append(path).append(File.separatorChar).append(saveFileName);
    	File dwldFile = new File(fileFullPath.toString());
    	
    	/** Set Response Header */
    	res.setHeader("Content-Type", "application/octet-stream; charset=UTF-8");
    	res.setContentLength((int) dwldFile.length());
    	res.setHeader("Content-Disposition", "attachment; fileName=" + "\"" + new String(originalFileName.getBytes("MS949"), "8859_1") + "\"" + ";");
    	res.setHeader("Content-Transfer-Encoding", "binary");
    	OutputStream os = res.getOutputStream();
    	InputStream is = null;
    	is = new FileInputStream(dwldFile);
    	FileCopyUtils.copy(is, os);
    	os.flush();
    }
  
    /**
     * 저장될 파일명의 이름을 변경해준다.
     * <xmp>
     * 예)dd.jpg  -> dd_1121245206883.jpg  (nano seconds)
     * </xmp>
     *
     * @param fileName
     * @return
     */
    public static String changeToSaveFileName(String fileName) {
        String[] fileNameSplit = fileName.split("\\.");
        fileNameSplit[fileNameSplit.length - 2] = fileNameSplit[fileNameSplit.length - 2] + "_" + System.nanoTime();
        StringBuilder sb = new StringBuilder();
        for (String split : fileNameSplit) {
            sb.append(split + ".");
        }
        return String.valueOf(System.nanoTime()) + "." + fileNameSplit[fileNameSplit.length - 1];
    }

    /**
     * 저장될 파일명의 이름을 변경해준다.
     * <xmp>
     * 예)새 텍스트 문서.txt  -> 1121245206883.txt  (nano seconds)
     * </xmp>
     *
     * @param fileName
     * @return
     */
    public static String changeToSaveFileNanoName(String fileName) {
        String[] fileNameSplit = fileName.split("\\.");
        fileNameSplit[fileNameSplit.length - 2] = System.nanoTime() + "";
        StringBuilder sb = new StringBuilder();
        for (String split : fileNameSplit) {
            sb.append(split + ".");
        }

        return String.valueOf(System.nanoTime()) + "." + fileNameSplit[fileNameSplit.length - 1];
    }

    /**
     * 파일의 확장자를 체크한다.
     *
     * @param fileName 파일이름
     * @return
     */
    public static String checkExtension(String fileName) throws Exception{
        String[] fileNameSplit = fileName.split("\\.");
        String extension = fileNameSplit[fileNameSplit.length - 1];

        if (imageExtensions.contains(extension.toLowerCase())) {
            return "IMG";
        } else if (exeExtensions.contains(extension.toLowerCase())) {
            return "EXE";
        } else if (docExtensions.contains(extension.toLowerCase())) {
            return "DOC";
        } else if (widgetExtensions.contains(extension.toLowerCase())) {
            return "WGT";
        } else if (etcExtensions.contains(extension.toLowerCase())) {
            return "ETC";
        } else if (zipExtensions.contains(extension.toLowerCase())) {
            return "ZIP";
        } else if((extension.toLowerCase()).equals("html")){
        	return "HTML";
        }else {
            //throw new FileExtensionException();
        	throw new Exception("Not allowed extension!");
        }
    }

    /**
     * 파일의 확장자를 리턴한다.
     *
     * @param fileName 파일이름
     * @return
     */
    public static String getFileExtension(String fileName) throws Exception{
    	String[] fileNameSplit = fileName.split("\\.");
    	String extension = fileNameSplit[fileNameSplit.length - 1];
    	return extension;
    }

    /**
     * 엑셀파일 다운로드
     *
     * @param request  HttpServletRequest
     * @param response HttpServletResponse
     * @param keyList  각 컬럼명
     * @param list     값(리스트)
     * @param fileName 저장될 파일명
     */
    public static void downloadExcelFile(HttpServletRequest request, HttpServletResponse response, String[] keyList, List<Map<String, String>> list, String fileName)
            throws Exception {

        if (keyList == null || keyList.length == 0 || list == null || list.isEmpty()) {            // Excel 생성 불가
            throw new NullPointerException();
        }

        StringBuffer excelDATA = new StringBuffer("<table>"); // excelDATA <table>형식으로 리스트 저장

        for (Map<String, String> map : list) {                  // 컬럼명(KeyList), 값List를 테이블형태로 저장
            excelDATA.append("<tr>");
            for (String key : keyList) {
                excelDATA.append("<td>");
                excelDATA.append(map.get(key));
                excelDATA.append("</td>");
            }
            excelDATA.append("</tr>");
        }
        excelDATA.append("</table>");

        // 헤더값 셋팅
        response.setContentType("application/vnd.ms-excel; charset=euc-kr");
        response.setHeader("Content-Disposition", "attachment; filename=\"" + new String(fileName.getBytes("MS949"), "8859_1") + ".xls;\""); //한글 파일명을 위한 파일명 인코딩
        response.setHeader("Content-Description", "JSP Generated Data");

        PrintWriter writer = null; // 받아온 데이터로 xml파일쓰기
        try {
            writer = response.getWriter();
            writer.write(excelDATA.toString());
        } finally {
            if (writer != null) {
                writer.close();
            }
        }
    }
    
}
